<!DOCTYPE html>
<html lang="en" data-bs-theme="auto">

<head>
  <%- header %>
    <style>
      .config-page {
        padding: 1em;
        border: 1px solid #dee2e6;
        border-top: none;
      }

      .buttons {
        padding: 1em 0;
      }
    </style>
</head>

<body id="app" v-cloak>
  <Navbar></Navbar>
  <div class="container">
    <h1 class="my-4">{{ $t('config.configuration') }}</h1>
    <div class="form" v-if="config">
      <!-- Header -->
      <ul class="nav nav-tabs">
        <li class="nav-item" v-for="tab in tabs" :key="tab.id">
          <a class="nav-link" :class="{'active': tab.id === currentTab}" href="#"
            @click="currentTab = tab.id">{{tab.name}}</a>
        </li>
      </ul>

      <!-- General Tab -->
      <general
        v-if="currentTab === 'general'"
        :config="config"
        :global-prep-cmd="global_prep_cmd"
        :platform="platform">
      </general>

      <!-- Input Tab -->
      <inputs
        v-if="currentTab === 'input'"
        :config="config"
        :platform="platform">
      </inputs>

      <!-- Audio/Video Tab -->
      <audio-video
        v-if="currentTab === 'av'"
        :config="config"
        :platform="platform"
      >
      </audio-video>

      <!-- Network Tab -->
      <network
        v-if="currentTab === 'network'"
        :config="config"
        :platform="platform">
      </network>

      <!-- Files Tab -->
      <files
        v-if="currentTab === 'files'"
        :config="config"
        :platform="platform">
      </files>

      <!-- Advanced Tab -->
      <advanced
        v-if="currentTab === 'advanced'"
        :config="config"
        :platform="platform">
      </advanced>

      <container-encoders
        :current-tab="currentTab"
        :config="config"
        :platform="platform">
      </container-encoders>
    </div>

    <!-- Save and Apply buttons -->
    <div class="alert alert-success my-4" v-if="saved && !restarted">
      <b>{{ $t('_common.success') }}</b> {{ $t('config.apply_note') }}
    </div>
    <div class="alert alert-success my-4" v-if="restarted">
      <b>{{ $t('_common.success') }}</b> {{ $t('config.restart_note') }}
    </div>
    <div class="mb-3 buttons">
      <button class="btn btn-primary" @click="save">{{ $t('_common.save') }}</button>
      <button class="btn btn-success" @click="apply" v-if="saved && !restarted">{{ $t('_common.apply') }}</button>
    </div>
  </div>
</body>


<script type="module">
  import { computed, createApp } from 'vue'
  import { initApp } from './init'
  import Navbar from './Navbar.vue'
  import General from './configs/tabs/General.vue'
  import Inputs from './configs/tabs/Inputs.vue'
  import Network from './configs/tabs/Network.vue'
  import Files from './configs/tabs/Files.vue'
  import Advanced from './configs/tabs/Advanced.vue'
  import AudioVideo from './configs/tabs/AudioVideo.vue'
  import ContainerEncoders from './configs/tabs/ContainerEncoders.vue'
  import {$tp, usePlatformI18n} from './platform-i18n'

  const app = createApp({
    components: {
      Navbar,
      General,
      Inputs,
      Network,
      Files,
      Advanced,
      // They will be accessible via audio-video, container-encoders only.
      AudioVideo,
      ContainerEncoders,
    },
    data() {
      return {
        platform: "",
        saved: false,
        restarted: false,
        config: null,
        currentTab: "general",
        global_prep_cmd: [],
        tabs: [ // TODO: Move the options to each Component instead, encapsulate.
          {
            id: "general",
            name: "General",
            options: {
              "locale": "en",
              "sunshine_name": "",
              "min_log_level": 2,
              "global_prep_cmd": "[]",
              "notify_pre_releases": "disabled",
            },
          },
          {
            id: "input",
            name: "Input",
            options: {
              "controller": "enabled",
              "gamepad": "auto",
              "ds4_back_as_touchpad_click": "enabled",
              "motion_as_ds4": "enabled",
              "touchpad_as_ds4": "enabled",
              "back_button_timeout": -1,
              "keyboard": "enabled",
              "key_repeat_delay": 500,
              "key_repeat_frequency": 24.9,
              "always_send_scancodes": "enabled",
              "key_rightalt_to_key_win": "disabled",
              "mouse": "enabled",
              "high_resolution_scrolling": "enabled",
              "native_pen_touch": "enabled",
              "keybindings": "[0x10,0xA0,0x11,0xA2,0x12,0xA4]",  // todo: add this to UI
            },
          },
          {
            id: "av",
            name: "Audio/Video",
            options: {
              "audio_sink": "",
              "virtual_sink": "",
              "install_steam_audio_drivers": "enabled",
              "adapter_name": "",
              "output_name": "",
              "min_fps_factor": 1,
            },
          },
          {
            id: "network",
            name: "Network",
            options: {
              "upnp": "disabled",
              "address_family": "ipv4",
              "port": 47989,
              "origin_web_ui_allowed": "lan",
              "external_ip": "",
              "lan_encryption_mode": 0,
              "wan_encryption_mode": 1,
              "ping_timeout": 10000,
            },
          },
          {
            id: "files",
            name: "Config Files",
            options: {
              "file_apps": "",
              "credentials_file": "",
              "log_path": "",
              "pkey": "",
              "cert": "",
              "file_state": "",
            },
          },
          {
            id: "advanced",
            name: "Advanced",
            options: {
              "fec_percentage": 20,
              "qp": 28,
              "min_threads": 2,
              "hevc_mode": 0,
              "av1_mode": 0,
              "capture": "",
              "encoder": "",
            },
          },
          {
            id: "nv",
            name: "NVIDIA NVENC Encoder",
            options: {
              "nvenc_preset": 1,
              "nvenc_twopass": "quarter_res",
              "nvenc_spatial_aq": "disabled",
              "nvenc_vbv_increase": 0,
              "nvenc_realtime_hags": "enabled",
              "nvenc_latency_over_power": "enabled",
              "nvenc_opengl_vulkan_on_dxgi": "enabled",
              "nvenc_h264_cavlc": "disabled",
            },
          },
          {
            id: "qsv",
            name: "Intel QuickSync Encoder",
            options: {
              "qsv_preset": "medium",
              "qsv_coder": "auto",
              "qsv_slow_hevc": "disabled",
            },
          },
          {
            id: "amd",
            name: "AMD AMF Encoder",
            options: {
              "amd_usage": "ultralowlatency",
              "amd_rc": "vbr_latency",
              "amd_enforce_hrd": "disabled",
              "amd_quality": "balanced",
              "amd_preanalysis": "disabled",
              "amd_vbaq": "enabled",
              "amd_coder": "auto",
            },
          },
          {
            id: "vt",
            name: "VideoToolbox Encoder",
            options: {
              "vt_coder": "auto",
              "vt_software": "auto",
              "vt_realtime": "enabled",
            },
          },
          {
            id: "vaapi",
            name: "VA-API Encoder",
            options: {
              "vaapi_strict_rc_buffer": "disabled",
            },
          },
          {
            id: "sw",
            name: "Software Encoder",
            options: {
              "sw_preset": "superfast",
              "sw_tune": "zerolatency",
            },
          },
        ],
      };
    },
    provide() {
       return {
         platform: computed(() => this.platform)
       }
    },
    created() {
      fetch("./api/config")
        .then((r) => r.json())
        .then((r) => {
          this.config = r;
          this.platform = this.config.platform;

          var app = document.getElementById("app");
          if (this.platform === "windows") {
            this.tabs = this.tabs.filter((el) => {
              return el.id !== "vt" && el.id !== "vaapi";
            });
          }
          if (this.platform === "linux") {
            this.tabs = this.tabs.filter((el) => {
              return el.id !== "amd" && el.id !== "qsv" && el.id !== "vt";
            });
          }
          if (this.platform === "macos") {
            this.tabs = this.tabs.filter((el) => {
              return el.id !== "amd" && el.id !== "nv" && el.id !== "qsv" && el.id !== "vaapi";
            });
          }

          // remove values we don't want in the config file
          delete this.config.platform;
          delete this.config.status;
          delete this.config.version;

          // TODO: let each tab's Component handle it's own data instead of doing it here

          // Populate default values from tabs options
          this.tabs.forEach(tab => {
            Object.keys(tab.options).forEach(optionKey => {
              if (this.config[optionKey] === undefined) {
                this.config[optionKey] = tab.options[optionKey];
              }
            });
          });

          this.config.global_prep_cmd = this.config.global_prep_cmd || [];
          this.global_prep_cmd = JSON.parse(this.config.global_prep_cmd);
        });
    },
    methods: {
      forceUpdate() {
        this.$forceUpdate()
      },
      serialize() {
        this.config.global_prep_cmd = JSON.stringify(this.global_prep_cmd);
      },
      save() {
        this.saved = false;
        this.restarted = false;
        this.serialize();

        // create a temp copy of this.config to use for the post request
        let config = JSON.parse(JSON.stringify(this.config))

        // delete default values from this.config
        this.tabs.forEach(tab => {
          Object.keys(tab.options).forEach(optionKey => {
            let delete_value = false

            if (["global_prep_cmd"].includes(optionKey)) {
              let config_value, default_value

              config_value = JSON.parse(config[optionKey])
              default_value = JSON.parse(tab.options[optionKey])

              if (config_value === default_value) {
                delete_value = true
              }
            }

            // todo: add proper type checking
            if (String(config[optionKey]) === String(tab.options[optionKey])) {
              delete_value = true
            }

            if (delete_value) {
              delete config[optionKey]
            }
          });
        });

        return fetch("./api/config", {
          method: "POST",
          body: JSON.stringify(config),
        }).then((r) => {
          if (r.status === 200) {
            this.saved = true
            return this.saved
          }
          else {
            return false
          }
        });
      },
      apply() {
        this.saved = this.restarted = false;
        let saved = this.save();

        saved.then((result) => {
          if (result === true) {
            this.restarted = true;
            setTimeout(() => {
              this.saved = this.restarted = false;
            }, 5000);
            fetch("./api/restart", {
              method: "POST"
            });
          }
        });
      },
    },
    mounted() {
      // Handle hashchange events
      const handleHash = () => {
        let hash = window.location.hash;
        if (hash) {
          // remove the # from the hash
          let stripped_hash = hash.substring(1);

          this.tabs.forEach(tab => {
            Object.keys(tab.options).forEach(key => {
              if (tab.id === stripped_hash || key === stripped_hash) {
                this.currentTab = tab.id;
              }
              if (key === stripped_hash) {
                // sleep for 2 seconds to allow the page to load
                setTimeout(() => {
                  let element = document.getElementById(stripped_hash);
                  if (element) {
                    window.location.hash = hash;
                  }
                }, 2000);
              }

              if (this.currentTab === tab.id) {
                // stop looping
                return true;
              }
            });
          });
        }
      };

      // Call handleHash for the initial load
      handleHash();

      // Add hashchange event listener
      window.addEventListener("hashchange", handleHash);
    },
  });

  initApp(app);
</script>
